package com.imachen.ymcc.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.imachen.ymcc.domain.Course;
import com.imachen.ymcc.domain.CourseMarket;
import com.imachen.ymcc.domain.CourseOrder;
import com.imachen.ymcc.domain.CourseOrderItem;
import com.imachen.ymcc.dto.*;
import com.imachen.ymcc.enums.GlobalExceptionCode;
import com.imachen.ymcc.fegin.CourseFeignClien;
import com.imachen.ymcc.mapper.CourseOrderMapper;
import com.imachen.ymcc.result.JSONResult;
import com.imachen.ymcc.service.ICourseOrderItemService;
import com.imachen.ymcc.service.ICourseOrderService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.imachen.ymcc.util.AssertUtil;
import com.imachen.ymcc.util.CodeGenerateUtils;
import com.imachen.ymcc.vo.CourseInfoVo;
import com.imachen.ymcc.vo.CourseOrderVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author imachen
 * @since 2022-10-16
 */
@Service
@Slf4j
public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {

    private String redisOrderToken = "orderToken:%s:%s";

    //购买课程 [java大神班;] , 支付[100.00] 元
    private String title = "购买课程 [%s;] , 支付[%S] 元";

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Autowired
    private CourseFeignClien courseFeignClien;

    @Autowired
    private ICourseOrderItemService courseOrderItemService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 课程下单
     * @param courseOrderDto
     */
    @Override
    public String placeOrder(CourseOrderDto courseOrderDto) {
        List<Long> courseIds = courseOrderDto.getCourseIds();
        String token = courseOrderDto.getToken();
        Integer payType = courseOrderDto.getPayType();
        // JSR303参数校验
        // 校验防重token,
        String idsStr = courseIds.stream().map(id -> String.valueOf(id)).collect(Collectors.joining(","));
        Long loginId = 3L;
        String redisKey = String.format(redisOrderToken,loginId,idsStr);
        // 获取redis中的key值
        Object redisToken = redisTemplate.opsForValue().get(redisKey);
        AssertUtil.isNotNull(redisToken, GlobalExceptionCode.ORDER_TOKEN_IS_NULL_ERROR);
        AssertUtil.isEquals(token,redisToken.toString(),GlobalExceptionCode.ORDER_TOKEN_ERROR);

        // 复用课程订单信息接口 查询课程信息和总价格
        JSONResult courseOrderInfo = courseFeignClien.getCourseOrderInfo(idsStr);
        AssertUtil.isTrue(courseOrderInfo.isSuccess(),GlobalExceptionCode.COURSE_IS_NULL_ERROR);
        Object courseOrderInfoData = courseOrderInfo.getData();
        AssertUtil.isNotNull(courseOrderInfoData,GlobalExceptionCode.COURSE_IS_NULL_ERROR);
        CourseOrderVo courseOrderVo = JSON.parseObject(JSON.toJSONString(courseOrderInfoData), CourseOrderVo.class);

        // 保存订单和订单快照
        List<CourseInfoVo> courseInfos = courseOrderVo.getCourseInfos();
        BigDecimal totalAmount = courseOrderVo.getTotalAmount();
        Date date = new Date();
        CourseOrder courseOrder = new CourseOrder();
        courseOrder.setCreateTime(date);
        courseOrder.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId));
        courseOrder.setTotalAmount(totalAmount);
        courseOrder.setTotalCount(courseIds.size());
        courseOrder.setStatusOrder(CourseOrder.WAIT_PAY_STATUS);
        courseOrder.setUserId(loginId);
        courseOrder.setVersion(0);
        courseOrder.setPayType(payType);
//        insert(courseOrder);

        // 订单快照
        courseInfos.forEach(courseInfoVo->{
            Course course = courseInfoVo.getCourse();
            CourseMarket courseMarket = courseInfoVo.getCourseMarket();
            CourseOrderItem courseOrderItem = new CourseOrderItem();
//            courseOrderItem.setOrderId(courseOrder.getId());
            courseOrderItem.setAmount(courseMarket.getPrice());
            courseOrderItem.setCount(1);
            courseOrderItem.setCreateTime(date);
            courseOrderItem.setCourseId(course.getId());
            courseOrderItem.setCourseName(course.getName());
            courseOrderItem.setCoursePic(course.getPic());
            courseOrderItem.setVersion(0);
            courseOrderItem.setOrderNo(courseOrder.getOrderNo());
//            courseOrderItemService.insert(courseOrderItem);
            // 设置标题并修改主订单
            String titleStr = String.format(title, course.getName(), totalAmount);
            courseOrder.setTitle(titleStr);
//            updateById(courseOrder);
        });

        // 生成支付参数对象
        PayDto payDto = new PayDto();
        BeanUtils.copyProperties(courseOrder, payDto);

        // =====扩展参数处理=====
        PayExtParamDto payExtParamDto = new PayExtParamDto();
        payExtParamDto.setCourseIds(idsStr);
        payExtParamDto.setOrderNo(courseOrder.getOrderNo());
        payExtParamDto.setUserId(loginId);
        payDto.setExtParams(JSON.toJSONString(payExtParamDto));

        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payDto)).build();
        // 发送mq事务消息
        TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
                "tx-course-order-group",
                "tx-course-order-topic:tx-course-order-tag",
                message,
                JSON.toJSONString(courseOrder));
        Boolean sendOk = result.getSendStatus() == SendStatus.SEND_OK;
        AssertUtil.isTrue(sendOk, GlobalExceptionCode.ORDER_SEND_FAIL_ERROR);

        // 下单完成后删除Redis中的token
        redisTemplate.delete(redisKey);

        try {
            // 发送延迟消息
            Message<String> delayMessage = MessageBuilder.withPayload(courseOrder.getOrderNo()).build();
            SendResult syncResult = rocketMQTemplate.syncSend(
                    "tx-order-delay-topic:tx-order-delay-tag",
                    delayMessage,
                    3000,
                    5
            );
            if (!syncResult.getSendStatus().equals(SendStatus.SEND_OK)) {
                // 可设置重试多次，不行再找管理员。。。
                log.error("下单发送延迟消息失败，订单号为：{}", courseOrder.getOrderNo());
            }
            log.info("下单发送延迟消息成功，订单号为：{}", courseOrder.getOrderNo());
        } catch (Exception e) {
            log.error("下单发送延迟消息失败，订单号为：{}", courseOrder.getOrderNo());
            e.printStackTrace();
        }

        return courseOrder.getOrderNo();
    }

    /**
     * 根据订单号查询信息
     * @param orderNo
     * @return
     */
    @Override
    public CourseOrder selectOneByOrderNo(String orderNo) {
        return selectOne(new EntityWrapper<CourseOrder>().eq("order_no",orderNo));
    }


    /**
     * 保存订单和订单快照信息
     * @param courseOrder
     */
    @Override
    public void saveOrderAndItem(CourseOrder courseOrder) {
        // 查询订单是否存在
        CourseOrder selectCourseOrder = selectOneByOrderNo(courseOrder.getOrderNo());
        AssertUtil.isNull(selectCourseOrder, GlobalExceptionCode.COURSE_ORDER_EXISTED_ERROR);
        // 保存课程订单
        insert(courseOrder);
        courseOrder.getCourseOrderItems().forEach(courseOrderItem -> {
            courseOrderItem.setOrderId(courseOrder.getId());
            courseOrderItemService.insert(courseOrderItem);
        });
    }


    /**
     * 课程秒杀下单
     * @param killCourseOrderDto
     * @return
     */
    @Override
    public String killPlaceOrder(KillCourseOrderDto killCourseOrderDto) {

        String orderNo = killCourseOrderDto.getOrderNo();
        Integer payType = killCourseOrderDto.getPayType();
        String token = killCourseOrderDto.getToken();


        // 3.去Redis中获取预创订单
        Object killOrderTemp = redisTemplate.opsForValue().get(orderNo);
        AssertUtil.isNotNull(killOrderTemp, GlobalExceptionCode.KILL_BEFORE_ORDER_IS_NULL_ERROR);
        KillPreOrderParamDto killPreOrderParamDto = JSON.parseObject(JSON.toJSONString(killOrderTemp), KillPreOrderParamDto.class);
        Long courseId = killPreOrderParamDto.getCourseId();

        // JSR303参数校验
        // 校验防重token,
        Long loginId = 3L;
        String redisKey = String.format(redisOrderToken,loginId,courseId);
        // 获取redis中的key值
        Object redisToken = redisTemplate.opsForValue().get(redisKey);
        AssertUtil.isNotNull(redisToken, GlobalExceptionCode.ORDER_TOKEN_IS_NULL_ERROR);
        AssertUtil.isEquals(token,redisToken.toString(),GlobalExceptionCode.ORDER_TOKEN_ERROR);

        // 保存订单和订单快照
        Date date = new Date();
        CourseOrder courseOrder = new CourseOrder();
        courseOrder.setCreateTime(date);
        courseOrder.setStatusOrder(CourseOrder.WAIT_PAY_STATUS);
        courseOrder.setOrderNo(orderNo);
        courseOrder.setTotalAmount(killPreOrderParamDto.getTotalAmount());
        courseOrder.setTotalCount(killPreOrderParamDto.getTotalCount());
        courseOrder.setUserId(loginId);
        courseOrder.setVersion(0);
        courseOrder.setPayType(payType);

        // 订单快照
        CourseOrderItem courseOrderItem = new CourseOrderItem();
        courseOrderItem.setAmount(killPreOrderParamDto.getTotalAmount());
        courseOrderItem.setCount(1);
        courseOrderItem.setCreateTime(date);
        courseOrderItem.setCourseId(killPreOrderParamDto.getCourseId());
        courseOrderItem.setCourseName(killPreOrderParamDto.getCourseName());
        courseOrderItem.setCoursePic(killPreOrderParamDto.getCoursePic());
        courseOrderItem.setVersion(0);
        courseOrderItem.setOrderNo(orderNo);

        courseOrder.getCourseOrderItems().add(courseOrderItem);
        // 设置标题并修改主订单
        String title = "购买课程 ["+killPreOrderParamDto.getCourseName()+"] , 支付["+killPreOrderParamDto.getTotalAmount()+"] 元";
        courseOrder.setTitle(title);

        // 生成支付参数对象
        PayDto payDto = new PayDto();
        BeanUtils.copyProperties(courseOrder, payDto);

        // =====扩展参数处理=====
        PayExtParamDto payExtParamDto = new PayExtParamDto();
        payExtParamDto.setCourseIds(killPreOrderParamDto.getCourseId().toString());
        payExtParamDto.setOrderNo(courseOrder.getOrderNo());
        payExtParamDto.setUserId(loginId);
        payDto.setExtParams(JSON.toJSONString(payExtParamDto));

        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payDto)).build();
        // 发送mq事务消息
        TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
                "tx-course-order-group",
                "tx-course-order-topic:tx-course-order-tag",
                message,
                JSON.toJSONString(courseOrder));
        Boolean sendOk = result.getSendStatus() == SendStatus.SEND_OK;
        AssertUtil.isTrue(sendOk, GlobalExceptionCode.ORDER_SEND_FAIL_ERROR);

        // 下单完成后删除Redis中的token
        redisTemplate.delete(redisKey);

        try {
            // 发送延迟消息
            Message<String> delayMessage = MessageBuilder.withPayload(courseOrder.getOrderNo()).build();
            SendResult syncResult = rocketMQTemplate.syncSend(
                    "tx-order-delay-topic:tx-order-delay-tag",
                    delayMessage,
                    3000,
                    5
            );
            if (!syncResult.getSendStatus().equals(SendStatus.SEND_OK)) {
                // 可设置重试多次，不行再找管理员。。。
                log.error("下单发送延迟消息失败，订单号为：{}", courseOrder.getOrderNo());
            }
            log.info("下单发送延迟消息成功，订单号为：{}", courseOrder.getOrderNo());
        } catch (Exception e) {
            log.error("下单发送延迟消息失败，订单号为：{}", courseOrder.getOrderNo());
            e.printStackTrace();
        }


        return courseOrder.getOrderNo();
    }
}
